﻿using System;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.PlatformAbstractions;
using Swashbuckle.AspNetCore.Swagger;
using Sykj.Components;
using System.IO;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Sykj.Infrastructure;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using NLog.Web;
using System.Text.Encodings.Web;
using System.Text.Unicode;

namespace Sykj.Web
{
    /// <summary>
    /// 程序启动
    /// </summary>
    public class Startup
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configuration">配置文件</param>
        /// <param name="env">环境参数</param>
        public Startup(IConfiguration configuration, IHostingEnvironment env)
        {
            Configuration = configuration;

            BaseConfig.SetBaseConfig(Configuration, env.ContentRootPath, env.WebRootPath);
        }

        /// <summary>
        /// 配置
        /// </summary>
        public IConfiguration Configuration { get; }

        /// <summary>
        ///  注入服务
        /// </summary>
        /// <param name="services"></param>
        public void ConfigureServices(IServiceCollection services)
        {
            #region 启用数据库DbContext
            //依赖注入，取得DbContext实例 使用DbContext池，提高性能 
            //自定义EF输出日志，.UseLoggerFactory(new Sykj.Repository.EFLoggerFactory())
            services.AddDbContextPool<Sykj.Repository.SyDbContext>(options => options.UseMySql(Configuration.GetConnectionString("ConnectionString")));

            //注入数据
            services.AddDataService();
            #endregion

            #region 启用百度编辑器
            //启用百度编辑器
            services.AddUEditorService();
            #endregion

            #region 启用mvc服务
            //注册mvc服务
            services.AddMvc(options =>
            {
                options.Filters.Add<LogExceptionFilter>();//设置全局异常处理过滤器
            }).AddJsonOptions(options =>
            {//json序列化设置
                //json序列化设置默认驼峰命名
                options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
                options.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
                //设置时间格式
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
            });
            #endregion

            #region 启用缓存 读取配置是否使用哪种缓存模式
            //启用dotnet本地缓存服务
            services.AddMemoryCache();

            if (Convert.ToBoolean(Configuration["IsRedis"]))
            {
                services.AddSingleton<ICacheService, RedisCacheService>();

                //将Redis分布式缓存服务添加到服务中
                services.AddDistributedRedisCache(options =>
                {
                    //用于连接Redis的配置 
                    options.Configuration = Configuration["RedisConnectionString"];
                    //Redis实例名RedisDistributedCache
                    options.InstanceName = "RedisDistributedCache";
                });
            }
            else
            {
                services.AddSingleton<ICacheService, MemoryCacheService>();
            }
            #endregion

            #region 启用Session服务
            //启用Session服务，启用redis，自动开始分布式session
            services.AddSession();
            #endregion

            #region 启用认证

            //认证
            //使用多个身份验证方案，设置默认方案
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            //Cookie 的验证 
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
             {
                 options.LoginPath = new PathString(string.Format("/{0}/login/logout", Constant.AREAMANAGER));
                 //options.LogoutPath = new PathString(string.Format("/{0}/login/logout", Constant.AREAMANAGER));
                 options.AccessDeniedPath = new PathString(string.Format("/{0}/home/noright", Constant.AREAMANAGER));
                 options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
             })
            //Jwt 的验证
            .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
            {
                JwtAuthConfigModel jwtConfig = new JwtAuthConfigModel();
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.JWTSecretKey)),//签名秘钥

                    ValidIssuer = jwtConfig.Issuer,//Token颁发机构
                    ValidateIssuer = true,

                    ValidAudience = jwtConfig.Audience,//颁发给谁
                    ValidateAudience = true,

                    // 是否验证Token有效期，使用当前时间与Token的Claims中的NotBefore和Expires对比
                    ValidateLifetime = true,
                    // 允许的服务器时间偏移量
                    ClockSkew = TimeSpan.FromSeconds(100)
                };
            });

            #endregion

            #region 启用授权

            //配置权限策略
            IServiceProvider provider = services.BuildServiceProvider();
            //授权
            services.AddAuthorization(options =>
            {
                //后台基于Requirements的策略授权
                options.AddPolicy("Permission", policy =>
                {
                    policy.AuthenticationSchemes.Add(CookieAuthenticationDefaults.AuthenticationScheme);
                    policy.Requirements.Add(new PermissionRequirement(provider.GetService<Sykj.IServices.IUsers>()));
                });
            });

            //注入授权Handler
            services.AddSingleton<IAuthorizationHandler, PermissionHandler>();

            #endregion

            #region 启用Swagger API文档管理
            //启用Swagger文档管理
            services.AddSwaggerGen(options =>
            {
                //设置文档基本信息
                options.SwaggerDoc("v1", new Info { Title = "API说明文档", Version = "v1" });

                //加载注释文件
                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                var xmlPath = Path.Combine(basePath, "Sykj.Web.xml");
                options.IncludeXmlComments(xmlPath);

                var security = new Dictionary<string, IEnumerable<string>> { { "Sykj", new string[] { } }, };
                //添加一个必须的全局安全信息，和AddSecurityDefinition方法指定的方案名称要一致，这里是Sykj。
                options.AddSecurityRequirement(security);
                options.AddSecurityDefinition("Sykj", new ApiKeyScheme
                {
                    Description = "JWT授权(数据将在请求头中进行传输) 参数结构: \"Authorization: Bearer {token}\"",
                    //jwt默认的参数名称
                    Name = "Authorization",
                    //jwt默认存放Authorization信息的位置(请求头中)
                    In = "header",
                    Type = "apiKey"
                });
            });
            #endregion

            //解决视图输出内容中文编码问题
            services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));

            //解决视图输出内容中文编码问题
            services.AddCors();

            #region 设置系统的依赖注入的服务提供器
            //设置系统的依赖注入的服务提供器
            BaseConfig.ServiceProvider = services.BuildServiceProvider();
            #endregion
        }

        /// <summary>
        /// 调用中间件
        /// </summary>
        /// <param name="app"></param>
        /// <param name="env"></param>
        /// <param name="loggerFactory"></param>
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            //使用NLog创建日志工厂
            loggerFactory.AddNLog();
            //读取Nlog配置文件 
            env.ConfigureNLog("nlog.config");

            // Swagger 中间件
            app.UseSwagger();

            // SwaggerUI 中间件
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
            });

            app.UseStaticFiles();

            //自定义异常处理
            app.UseMiddleware<ExceptionMiddleware>();

            //验证中间件
            app.UseAuthentication();

            //跨域请求设置 
            app.UseCors(builder => builder
            //允许任何来源
            .AllowAnyOrigin()
            //所有请求方法
             .AllowAnyMethod()
             //所有请求头
             .AllowAnyHeader());

            //Session中间件
            app.UseSession();

            app.UseMvc(routes =>
            {
                //启用区域路由的配置
                routes.MapRoute(
                  name: "area",
                  template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
                );

                //全局控制器路由配置
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}
